<!DOCTYPE html>

<html>
  <head>
    <!-- allow fancy checkmarks to show up -->
    <meta charset="UTF-8" />

    <!-- complain about uncaught errors -->
    <script type="text/javascript">
      window.onerror = function () {
        document.documentElement.style.background = 'red';
      };
    </script>

    <!-- better living through jQuery -->
    <script type="text/javascript" src="support/jquery-1.5.2.js"></script>

    <!-- mocha test framework -->
    <script
      type="text/javascript"
      src="../node_modules/mocha/mocha.js"
    ></script>
    <link
      rel="stylesheet"
      type="text/css"
      href="../node_modules/mocha/mocha.css"
    />

    <!-- home-grown assertions -->
    <script src="support/assert.js" type="text/javascript"></script>

    <!-- configure mocha and chai -->
    <script type="text/javascript">
      var outputXunit = new URLSearchParams(location.search).get('xunit');

      mocha.setup({
        ui: 'tdd',
        reporter: outputXunit ? 'xunit' : 'html',
        bail: false,
      });
    </script>

    <!-- include the library with the tests inlined -->
    <script
      id="mathquill"
      type="text/javascript"
      src="../build/mathquill.test.js"
      onerror="alert('couldn\'t find `build/mathquill.test.js`. Please run `make test` before opening this file.')"
    ></script>

    <!-- include MathQuill-basic -->
    <link rel="stylesheet" type="text/css" href="../build/mathquill.css" />
    <script type="text/javascript" src="../build/mathquill-basic.js"></script>
    <script type="text/javascript">
      MQBasic = MathQuill.noConflict().getInterface(MathQuill.getInterface.MAX);
      MQ = MathQuill.getInterface(MathQuill.getInterface.MAX);
    </script>
  </head>

  <body>
    <h1>Unit Tests</h1>
    <div id="mocha"></div>
    <div id="mock"></div>
    <div id="qunit"></div>

    <script type="text/javascript">
      teardown(function () {
        $('#mock').empty();
      });

      function runXUnit() {
        var xunit = '';
        Mocha.process.stdout.write = function (line) {
          xunit += line;
        };

        var runner = mocha.run();
        runner.on('end', function () {
          setTimeout(function () {
            if (window.xunitCallback) xunitCallback(xunit);
          });
        });
      }

      function runJenkins() {
        var json = location.search.indexOf('json') >= 0;
        var listTests = location.search.indexOf('listTests') >= 0;
        var startTime = Date.now();
        var suiteMap = {};
        var runner = mocha.run();

        runner.on('suite', function (suite) {
          var title = xmlEscape(suite.fullTitle());
          suiteMap[title] = {
            assertions: [],
          };
          if (listTests) {
            suiteMap[title].assertions.push({
              elapsedTime: 0,
              timestamp: 0,
              result: true,
              message: 'okay',
            });
          }
        });

        runner.on('pass', function (test) {
          if (!listTests) {
            var title = getTestSuiteTitle(test);
            var elapsedTime = test.duration / 1000;
            var timestamp = Date.now();
            suiteMap[title].assertions.push({
              elapsedTime: elapsedTime,
              timestamp: timestamp,
              result: true,
              message: xmlEscape(test.title),
            });
          }
        });

        runner.on('fail', function (test, err) {
          if (!listTests) {
            var title = getTestSuiteTitle(test);
            var elapsedTime = test.duration / 1000;
            var timestamp = Date.now();
            suiteMap[title].assertions.push({
              elapsedTime: elapsedTime,
              timestamp: timestamp,
              result: false,
              message: xmlEscape(err.message),
              stacktrace: xmlEscape(err.stack),
              expected: true,
              actual: false,
            });
          }
        });

        runner.on('end', function () {
          var moduleResults = [];
          for (var suiteTitle in suiteMap) {
            if (suiteMap.hasOwnProperty(suiteTitle)) {
              var suiteResults = suiteMap[suiteTitle];
              var duration = 0;
              suiteResults.assertions.forEach(function (assertion) {
                duration += assertion.elapsedTime;
              });
              moduleResults.push({
                name: suiteTitle,
                assertions: suiteResults.assertions,
                time: duration,
              });
            }
          }
          var testResults = {
            modules: { mathquill: moduleResults },
            passes: runner.stats.passes,
            failures: runner.stats.failures,
            skips: 0,
          };
          if (json) {
            window.testResultsString = JSON.stringify(testResults, null, 2);
          } else {
            window.testResultsString = outputXML(testResults);
          }
        });

        function getTestSuiteTitle(test) {
          return xmlEscape(test.parent.fullTitle());
        }

        // must escape a few symbols in xml attributes:
        //http://stackoverflow.com/questions/866706/which-characters-are-invalid-unless-encoded-in-an-xml-attribute
        function xmlEscape(string) {
          string = string || '';
          string = string.replace(/&/g, '&amp;');
          string = string.replace(/"/g, '&quot;');
          string = string.replace(/</g, '&lt;');
          return string;
        }

        function outputXML(results) {
          var xml = [];
          xml.push('<?xml version="1.0"?>');
          xml.push('<testsuites>');

          for (var moduleName in results.modules) {
            var module = results.modules[moduleName];
            for (var i = 0; i < module.length; i++) {
              var test = module[i];
              xml.push(
                '<testsuite name="' +
                  moduleName +
                  '.' +
                  test.name +
                  '" time="' +
                  test.time +
                  '">'
              );

              for (var j = 0; j < test.assertions.length; j++) {
                var assertion = test.assertions[j];
                var assertionMessage =
                  assertion.message || 'no-assertion-message';
                var assertionTime = assertion.elapsedTime;

                xml.push(
                  '<testcase name="' +
                    assertionMessage +
                    '" time="' +
                    assertionTime +
                    '">'
                );

                if (assertion.result === false) {
                  xml.push('<failure message="' + assertionMessage + '">');
                  xml.push('Expected: ' + assertion.expected + '\n');
                  xml.push('Actual: ' + assertion.actual + '\n');
                  xml.push('Stacktrace: ' + assertion.stacktrace);
                  xml.push('</failure>');
                } else if (assertion.result === undefined) {
                  xml.push('<skipped />');
                }

                xml.push('</testcase>');
              }

              xml.push('</testsuite>');
            }
          }

          xml.push('</testsuites>');
          return xml.join('\n');
        }
      }

      if (outputXunit) {
        runXUnit();
      } else {
        runJenkins();
      }
    </script>
  </body>
</html>
